home *** CD-ROM | disk | FTP | other *** search
/ Magnum One / Magnum One (Mid-American Digital) (Disc Manufacturing).iso / d16 / winvn060.arc / WVUSENET.C < prev    next >
C/C++ Source or Header  |  1991-07-01  |  41KB  |  1,241 lines

  1. /* wvusenet.c */
  2. /*  This file consists of the window procedure and support routines
  3.  *  for the main window to the application WinVn.
  4.  */
  5.  
  6. #include "windows.h"
  7.  
  8. #ifndef MAC
  9. #include "winundoc.h"
  10. #include <stdlib.h>
  11. #endif
  12.  
  13. #include "WVglob.h"
  14. #include "WinVn.h"
  15. #ifdef MAC
  16. #include "MRRM1.h"
  17. #include <DialogMgr.h>
  18. #endif
  19.  
  20. /*--- FUNCTION: WinVnConfWndProc ---------------------------------------
  21.  *
  22.  *    Window procedure for the "Net" window.
  23.  *    This window displays the names of the newsgroups, one per line.
  24.  *
  25.  *    Entry    The usual parameters for any window function.
  26.  *
  27.  *    Note:    We don't do anything until "Initialized" is true.
  28.  *             This way, the user won't try to perform functions
  29.  *             while the communications are still being set up.
  30.  */
  31.  
  32. long FAR PASCAL WinVnConfWndProc(hWnd, message, wParam, lParam)
  33. HWND hWnd;
  34. unsigned message;
  35. WORD wParam;
  36. LONG lParam;
  37. {
  38. #ifndef MAC
  39.     FARPROC lpProcAbout;
  40.     HMENU hMenu;
  41. #endif
  42.     PAINTSTRUCT ps;                              /* paint structure          */
  43.    POINT ptCursor;
  44.  
  45.     HDC hDC;                                    /* handle to display context */
  46.     HWND hWndView;
  47.     HDC  hDCView;
  48.     RECT myRect;                                  /* selection rectangle      */
  49.  
  50.     int j, idoc;
  51.     int found;
  52.     char far *lpsz;
  53.     TypLine far *LinePtr, far *GroupLinePtr;
  54.     TypBlock far *BlockPtr, far *GroupBlockPtr;
  55.     int LinesGone;
  56.     int SelLine;
  57.    HANDLE hBlock;
  58.    TypDoc *MyDoc;
  59.    unsigned int Offset;
  60.  
  61.     int X, Y, nWidth, nHeight;
  62.    int docnum;
  63.    int newdoc;
  64.     char mybuf[MAXINTERNALLINE];
  65.     int mylen;
  66.     int OldSel = FALSE;
  67.     int CtrlState;
  68.    TypLineID MyLineID;
  69.  
  70.     switch (message) {
  71. #ifndef MAC
  72.         case WM_SYSCOMMAND:
  73.             if (wParam == ID_ABOUT) {
  74.                 lpProcAbout = MakeProcInstance(About, hInst);
  75.                 DialogBox(hInst, "AboutBox", hWnd, lpProcAbout);
  76.                 FreeProcInstance(lpProcAbout);
  77.                 break;
  78.             }
  79.             else
  80.                 return (DefWindowProc(hWnd, message, wParam, lParam));
  81. #endif
  82.         case WM_CREATE:
  83.             /* Set up the timer which will wake us up every so often
  84.              * so we can check to see if new characters have arrived from
  85.              * the server.
  86.              */
  87.  
  88. #ifndef MAC
  89.             idTimer =  SetTimer(hWnd, NULL, 1000, (FARPROC) NULL);
  90.  
  91.             /* Get handle to the hourglass cursor */
  92.  
  93.             hHourGlass = LoadCursor(hInst, IDC_WAIT);
  94.  
  95.             /* Add the "About" option to the system menu.            */
  96.  
  97.             hMenu = GetSystemMenu(hWnd, FALSE);
  98.             ChangeMenu(hMenu, NULL, NULL, NULL, MF_APPEND | MF_SEPARATOR);
  99.             ChangeMenu(hMenu, NULL, "A&bout WinVn...", ID_ABOUT,
  100.                 MF_APPEND | MF_STRING);
  101. #endif
  102.             /* Fill in the Net document's window handle that we
  103.              * were unable to fill in in the call to InitDoc.
  104.              */
  105.             NetDoc.hDocWnd = hWnd;
  106.             break;
  107.  
  108.         case WM_CHAR:
  109.             /* Hitting CR on a group name is the same as double-clicking
  110.              * on the name.
  111.              */
  112.             if(!Initializing) {
  113.                if (wParam == '\r') {
  114. #ifndef MAC
  115.                    GetCursorPos(&ptCursor);
  116.                    ScreenToClient(hWnd, &ptCursor);
  117.                    X = ptCursor.x;
  118.                    Y = ptCursor.y;
  119. #else
  120.                GetMouse(&ptCursor);
  121.                X = ptCursor.h;
  122.                Y = ptCursor.v;
  123. #endif
  124.                    goto getgroup;
  125.                }
  126.             }
  127.             break;
  128.  
  129.          case WM_KEYDOWN:
  130.             /* See if this key should be mapped to a scrolling event
  131.              * for which we have programmed the mouse.  If so,
  132.              * construct the appropriate mouse call and call the mouse code.
  133.              * Also, F6 means switch to next window.
  134.              * (This latter should also be table-driven.)
  135.              */
  136.             if(!Initializing) {
  137.                if(wParam == VK_F6) {
  138.                   NextWindow(&NetDoc);
  139.                } else {
  140.                   /* Based on the state of the Control key and the value
  141.                    * of the key just pressed, look up in the array
  142.                    * key2scroll to see if we should process this keystroke
  143.                    * as if it were a mouse event.  If so, simulate the
  144.                    * mouse event by sending the appropriate message.
  145.                    */
  146. #ifndef MAC
  147.                   CtrlState = GetKeyState(VK_CONTROL) < 0;
  148.                   for(j=0; j<NUMKEYS; j++) {
  149.                      if(wParam == key2scroll[j].wVirtKey &&
  150.                       CtrlState == key2scroll[j].CtlState) {
  151.                         SendMessage(hWnd,key2scroll[j].iMessage,
  152.                          key2scroll[j].wRequest,0L);
  153.                         break;
  154.                      }
  155.                   }
  156. #endif
  157.                }
  158.             }
  159.             break;
  160.  
  161.         case WM_LBUTTONDOWN:
  162.           /*  Clicking the left button on a group name toggles the
  163.            *  selected/not selected status of that group.
  164.            *  Currently selected groups are displayed in reverse video.
  165.            */
  166.             if(!Initializing) {
  167.                X = LOWORD(lParam);
  168.                Y = HIWORD(lParam);
  169.  
  170.                if(CursorToTextLine(X,Y,&NetDoc,&BlockPtr,&LinePtr)) {
  171.                   ( (TypGroup far *)( ((char far *)LinePtr) + sizeof(TypLine) ) )
  172.                    ->Selected ^= TRUE;
  173.                   GlobalUnlock(BlockPtr->hCurBlock);
  174.                }
  175.  
  176.                InvalidateRect(hWnd, NULL, FALSE);
  177.             }
  178.             break;
  179.  
  180.         case WM_LBUTTONDBLCLK:
  181.            /*  Double-clicking on a group name creates a "Group"
  182.             *  window, whose purpose is to display the subjects
  183.             *  of the articles in the group.
  184.             */
  185.             if(!Initializing) {
  186.                X = LOWORD(lParam);
  187.                Y = HIWORD(lParam);
  188.             getgroup:;
  189.  
  190.                if(CursorToTextLine(X,Y,&NetDoc,&GroupBlockPtr,&GroupLinePtr)) {
  191.                   if(MyDoc = (( (TypGroup far *)
  192.                     ( ((char far *)GroupLinePtr)+sizeof(TypLine) ) )->SubjDoc)) {
  193.  
  194.                      /* We already have a document containing the subjects */
  195.                      /* of this group, so just activate it.                */
  196.  
  197.                      SetActiveWindow(MyDoc->hDocWnd);
  198.                      SetFocus(MyDoc->hDocWnd);
  199.                      GlobalUnlock(GroupBlockPtr->hCurBlock);
  200.                      break;
  201.                   }
  202.                   if(CommBusy) {
  203.                      GlobalUnlock(GroupBlockPtr->hCurBlock);
  204.                      MessageBox(hWnd,"Sorry, I am already busy retrieving information from the server.\n\
  205. Try again in a little while.","Can't request info on group",MB_OK|MB_ICONASTERISK);
  206.                      break;
  207.                   }
  208.  
  209.                   /* At this point, we've determined that the subjects for
  210.                    * this newsgroup are not in memory, and that it's OK
  211.                    * to fetch them from the server.  (Comm line not busy.)
  212.                    * Figure out whether a new window/document should be
  213.                    * created for this group & set "newdoc" accordingly.
  214.                    */
  215.                   newdoc = FALSE;
  216.                   if(ViewNew || !ActiveGroupDoc || !(ActiveGroupDoc->InUse)) {
  217.                      found = FALSE;
  218.                      for(docnum=0; docnum<MAXGROUPWNDS; docnum++) {
  219.                        if(!GroupDocs[docnum].InUse) {
  220.                            found = TRUE;
  221.                            newdoc = TRUE;
  222.                            CommDoc = &(GroupDocs[docnum]);
  223.                            break;
  224.                         }
  225.                      }
  226.                      if(!found) {
  227.                         MessageBox(hWnd,"You have too many group windows \
  228. active;\nClose one or select 'Reuse Old Window'.","Can't open new window",MB_OK|MB_ICONASTERISK);
  229.                         UnlockLine(GroupBlockPtr,GroupLinePtr,&hBlock,&Offset,&MyLineID);
  230.                         break;
  231.                      }
  232.                   } else {
  233.                      /* Must reuse old window for this group.           */
  234.                      CommDoc = ActiveGroupDoc;
  235.                      UnlinkArtsInGroup(CommDoc);
  236.                      LockLine(CommDoc->hParentBlock,CommDoc->ParentOffset,CommDoc->ParentLineID,&BlockPtr,&LinePtr);
  237.                      ( (TypGroup far *)
  238.                       ( ((char far *)LinePtr)+sizeof(TypLine) ) )->SubjDoc = (TypDoc *)NULL;
  239.                      UnlockLine(BlockPtr,LinePtr,&hBlock,&Offset,&MyLineID);
  240.                      /* clear out contents of this document for reuse */
  241.                      FreeDoc(CommDoc);
  242.                   }
  243.  
  244.                   /* Create window title indicating we are retrieving text. */
  245.  
  246.                   CommDoc->InUse = TRUE;
  247.                   lpsz = (char far *) ( ((char far *)GroupLinePtr) +
  248.                    sizeof(TypLine)+ sizeof(TypGroup) ) ;
  249.                   strcpy(mybuf,"Retrieving ");
  250.                   lstrcat(mybuf,lpsz);
  251.  
  252.                   /* If necessary, create a new window.              */
  253.  
  254.                   if(newdoc) {
  255.                      hWndView = CreateWindow("WinVnView",
  256.                      mybuf,
  257.                      WS_OVERLAPPEDWINDOW | WS_VSCROLL  ,
  258.                      (int) (xScreen*7 / 20)+docnum*CharWidth+1,      /* Initial X pos */
  259.                      (int) docnum*LineHeight,
  260.                      (int) (xScreen * 5 / 8),     /* Initial X Width */
  261.                      (int) (yScreen * 3 / 8),  /* CW_USEDEFAULT, */
  262.                      NULL,
  263.                      NULL,
  264.                      hInst,
  265.                      NULL);
  266.  
  267.                      if (!hWndView) return (0);  /* ??? */
  268. #ifndef MAC
  269.                      ShowWindow(hWndView, SW_SHOWNORMAL);
  270. #endif
  271.                   } else {
  272.                      hWndView = CommDoc->hDocWnd;
  273.                      SetWindowText(hWndView,mybuf);
  274.                   }
  275.                   RcvLineCount = 0;
  276.                   TimesWndUpdated = 0;
  277.  
  278.                   /* Do some housekeeping:  Set group as selected,
  279.                    * initialize empty document to hold subject lines,
  280.                    * set focus to this document, set pointers linking
  281.                    * this document and the subject line.
  282.                    */
  283.                   ( (TypGroup far *)( ((char far *)GroupLinePtr) +
  284.                     sizeof(TypLine) ) )->Selected = TRUE;
  285.                   InitDoc(CommDoc,hWndView,&NetDoc,DOCTYPE_GROUP);
  286.                   PtrToOffset(GroupBlockPtr,GroupLinePtr,&(CommDoc->hParentBlock),
  287.                    &(CommDoc->ParentOffset),&(CommDoc->ParentLineID));
  288.                   SetActiveWindow(hWndView);
  289.                   SetFocus(hWndView);
  290.  
  291.                   ( (TypGroup far *)( ((char far *)GroupLinePtr) + sizeof(TypLine) ) )
  292.                    ->SubjDoc = CommDoc;
  293.                   GlobalUnlock(GroupBlockPtr->hCurBlock);
  294.                   InvalidateRect(hWndView,NULL,FALSE);
  295. #ifndef MAC
  296.                   UpdateWindow(hWndView);
  297. #endif
  298.                   InvalidateRect(NetDoc.hDocWnd,NULL,FALSE);
  299.  
  300.                   /* Deal with Comm-related stuff:  set FSA variables,
  301.                    * send the GROUP command to NNTP.
  302.                    */
  303.  
  304.                   CommLinePtr = CommLineIn;
  305.                   CommBusy = TRUE;
  306.                   CommState = ST_GROUP_RESP;
  307.                   strcpy(mybuf,"GROUP ");
  308.                   lpsz = (char far *)GroupLinePtr + sizeof(TypLine) + sizeof(TypGroup);
  309.                   lstrcat(mybuf,lpsz);
  310.                   mylstrncpy(CurrentGroup,lpsz,MAXFINDSTRING);
  311.                   /* lstrcat(mybuf,"\r"); */
  312.                   PutCommLine(mybuf,lstrlen(mybuf));
  313.                }
  314.             }
  315.             break;
  316.  
  317.         case WM_TIMER:
  318.             DoCommInput();
  319.             break;
  320.  
  321.         case WM_SIZE:
  322.             /* Store the new size of the window.                     */
  323.             GetClientRect(hWnd, &myRect);
  324.             NetDoc.ScXWidth =  myRect.right;
  325.             NetDoc.ScYHeight = myRect.bottom;
  326. #ifdef MAC
  327.             NetDoc.ScYLines = (myRect.bottom - myRect.top) / LineHeight;
  328. #else
  329.             NetDoc.ScYLines = (myRect.bottom - myRect.top - TopSpace) / LineHeight;
  330. #endif
  331.             NetDoc.ScXChars = (myRect.right - myRect.left - SideSpace) / CharWidth;
  332.             break;
  333.  
  334.         case WM_VSCROLL:
  335.             if(!Initializing) {
  336.                ScrollIt(&NetDoc,wParam,lParam);
  337.             }
  338.             break;
  339.  
  340.             case WM_COMMAND:
  341.             switch(wParam) {
  342.  
  343.               case IDM_QUIT:
  344.                 SaveNewsrc = FALSE;
  345.               case IDM_EXIT:
  346.                 DestroyWindow(hWnd);
  347.                 break;
  348.  
  349.               case IDM_VIEW_SEL_GROUP:
  350.                 break;
  351.  
  352.               case IDM_SHOW_SUBSCR:
  353.               case IDM_SHOW_ALL_GROUP:
  354.               case IDM_SEL_SUBSCR:
  355.               case IDM_SELECTALL:
  356.                 MessageBox(hWnd, "Command not implemented",
  357.                   "Sorry", MB_OK);
  358.                 break;
  359.  
  360.               case IDM_UNSEL_ALL:
  361.                 ForAllLines(&NetDoc,SetLineFlag,0,FALSE);
  362.                 InvalidateRect(hWnd,NULL,FALSE);
  363.                 break;
  364.  
  365.               case IDM_SUBSCRIBE:
  366.                 InitGroupTable();
  367.                 ForAllLines(&NetDoc,GroupAction,GROUP_ACTION_SUBSCRIBE,TRUE);
  368.                 MergeGroups(ADD_SUBSCRIBED_END_OF_SUB);
  369.                 CleanUpGroupTable();
  370.                      ForAllLines(&NetDoc,SetLineFlag,0,FALSE);
  371.                      ScreenToTop(&NetDoc);
  372.                 InvalidateRect(hWnd,NULL,FALSE);
  373.                 break;
  374.  
  375.               case IDM_UNSUBSCRIBE:
  376.                 InitGroupTable();
  377.                 ForAllLines(&NetDoc,GroupAction,GROUP_ACTION_UNSUBSCRIBE,FALSE);
  378.                 ShellSort(NewGroupTable,nNewGroups,sizeof(void far *),GroupCompare);
  379.                 MergeGroups(ADD_SUBSCRIBED_END_OF_SUB);
  380.                 CleanUpGroupTable();
  381.                 ForAllLines(&NetDoc,SetLineFlag,0,FALSE);
  382.                      ScreenToTop(&NetDoc);
  383.                      InvalidateRect(hWnd,NULL,FALSE);
  384.                 break;
  385.  
  386.               case IDM_GROUP_TOP:
  387.                 InitGroupTable();
  388.                 ForAllLines(&NetDoc,GroupAction,GROUP_ACTION_SUBSCRIBE,TRUE);
  389.                 MergeGroups(ADD_SUBSCRIBED_TOP_OF_DOC);
  390.                 CleanUpGroupTable();
  391.                 ForAllLines(&NetDoc,SetLineFlag,0,FALSE);
  392.                      ScreenToTop(&NetDoc);
  393.                      InvalidateRect(hWnd,NULL,FALSE);
  394.                 break;
  395.  
  396.               case IDM_NEW_WIN_GROUP:
  397.                 ViewNew = !ViewNew;
  398.                 CheckView(hWnd);
  399.                 break;
  400.  
  401.               case IDM_NEW_WIN_ARTICLE:
  402.                 NewArticleWindow = !NewArticleWindow;
  403.                 CheckView(hWnd);
  404.                 break;
  405.  
  406.               case IDM_COMMOPTIONS:
  407. #ifndef MAC
  408.                 if(DialogBox(hInst,"WinVnComm",hWnd,lpfnWinVnCommDlg)) {
  409.                   InvalidateRect(hWnd,NULL,TRUE);
  410.                 }
  411. #endif
  412.  
  413.                 break;
  414.  
  415.                case IDM_CONFIG_PERSONAL:
  416. #ifndef MAC
  417.                   DialogBox(hInst,"WinVnPersonal",hWnd,lpfnWinVnPersonalInfoDlg);
  418. #endif
  419.                   break;
  420.  
  421.                case IDM_CONFIG_MISC:
  422. #ifndef MAC
  423.                   DialogBox(hInst,"WinVnMisc",hWnd,lpfnWinVnMiscDlg);
  424. #endif
  425.                   break;
  426.  
  427.                case IDM_RESET:
  428.                   CommBusy = FALSE;
  429.                   CommState = ST_NONE;
  430.                   break;
  431.  
  432.                case IDM_FIND:
  433.                   FindDoc = &NetDoc;
  434. #ifndef MAC
  435.                   if(DialogBox(hInst,"WinVnFind",hWnd,lpfnWinVnFindDlg)) {
  436.                      found = DoFind(TRUE);
  437.                      if(!found) {
  438.                         strcpy(mybuf,"\"");
  439.                         strcat(mybuf,NetDoc.SearchStr);
  440.                         strcat(mybuf,"\" not found.");
  441.                         MessageBox(hWnd,mybuf,"Not found",MB_OK);
  442.                      }
  443.                   }
  444. #else
  445.                   {
  446.                   DialogPtr myDialog;
  447.                   BOOL dialogDone = FALSE, oktofind = FALSE;
  448.                   int itemhit;
  449.                   int itemType;
  450.                   Handle itemHandle;
  451.                   Rect itemRect;
  452.                   char myStr[255];
  453.  
  454.                   myDialog = GetNewDialog(DLOG_FIND,NULL,(WindowPtr)-1);
  455.                   GetDItem(myDialog,3,&itemType,&itemHandle,&itemRect);
  456.                   strcpy(myStr,FindDoc->SearchStr);
  457.                   CtoPstr(myStr);
  458.                   SetIText(itemHandle,myStr);
  459.                   ShowWindow((WindowPtr)myDialog);
  460.                   while(!dialogDone) {
  461.                      ModalDialog(NULL,&itemhit);
  462.                      switch(itemhit) {
  463.                         case OK:
  464.                            dialogDone = TRUE;
  465.                            oktofind = TRUE;
  466.                            break;
  467.                         case Cancel:
  468.                            dialogDone = TRUE;
  469.                            break;
  470.                      }
  471.                   }
  472.                   HideWindow(myDialog);
  473.                   if(oktofind) {
  474.                      GetIText(itemHandle,&myStr);
  475.                      PtoCstr(myStr);
  476.                      strcpy(FindDoc->SearchStr,(char *)myStr);
  477.                      found = DoFind(TRUE);
  478.                      if(!found) {
  479.                         strcpy(mybuf,"\"");
  480.                         strcat(mybuf,NetDoc.SearchStr);
  481.                         strcat(mybuf,"\" not found.");
  482.                         MessageBox(hWnd,mybuf,"Not found",MB_OK);
  483.                      }
  484.                   }
  485.                   }
  486. #endif
  487.                   break;
  488.  
  489.                case IDM_FIND_NEXT_SAME:
  490.                   FindDoc = &NetDoc;
  491.                   if(strcmp(FindDoc->SearchStr,"")) {
  492.                      found = DoFind(FALSE);
  493.                      if(!found) {
  494.                         strcpy(mybuf,"\"");
  495.                         strcat(mybuf,NetDoc.SearchStr);
  496.                         strcat(mybuf,"\" not found.");
  497.                         MessageBox(hWnd,mybuf,"No more occurrences",MB_OK);
  498.                      }
  499.                   }
  500.                   break;
  501. #ifndef MAC
  502.                case ID_ABOUT:
  503.                   lpProcAbout = MakeProcInstance(About, hInst);
  504.                   DialogBox(hInst, "AboutBox", hWnd, lpProcAbout);
  505.                   FreeProcInstance(lpProcAbout);
  506.                   break;
  507.  
  508.                case IDM_HELP:
  509.                   MakeHelpPathName(mybuf,MAXINTERNALLINE);
  510.                   WinHelp(hWndConf,mybuf,HELP_INDEX,0L);
  511.                   break;
  512. #endif
  513.  
  514.                case IDM_MAIL:
  515.                   CreatePostingWnd((TypDoc *)NULL,DOCTYPE_MAIL);
  516.                   break;
  517.             }
  518.             break;
  519.  
  520.         case WM_PAINT:
  521.             {
  522.             HANDLE hBlock;
  523.             unsigned int Offset, MyLen, width, indicatorwidth;
  524.             int VertLines, HorzChars;
  525.             int EndofDoc = FALSE;
  526.             int RangeHigh, CurPos;
  527.             int RestX,Xtext;
  528.             char far *textptr;
  529.             char *cptr, *cptr2, *indcptr;
  530.             char init_msg[60];
  531.             TypGroup far *MyGroup;
  532.             HANDLE hBlackBrush;
  533.             DWORD MyColors[4], MyBack[4];
  534.             DWORD Rop;
  535.             RECT myRect;
  536.             POINT myPoint;
  537.             int MyColorMask = 1, PrevColorMask = MyColorMask;
  538. #define SUBSCR_MASK 1
  539. #define SELECT_MASK 2
  540.  
  541.             hDC = BeginPaint (hWnd, &ps);
  542.             GetClientRect(hWnd, &myRect);
  543.             SelectObject(hDC,hFont);
  544.  
  545.             /* If still initializing comm link, put out a message    */
  546.             /* to that effect.                                       */
  547.  
  548.             switch(Initializing) {
  549.              case INIT_ESTAB_CONN:
  550.                cptr = "Establishing link";
  551.                cptr2 = "to news server...";
  552.  
  553.                goto display_msg;
  554.  
  555.              case INIT_READING_NEWSRC:
  556.                cptr = "Reading your log";
  557.                cptr2 = "(\"newsrc\")...";
  558.                goto display_msg;
  559.  
  560.              case INIT_SCANNING_NETDOC:
  561.                cptr = "Creating hash table";
  562.                cptr2 = "from current groups...";
  563.                goto display_msg;
  564.  
  565.              case INIT_GETTING_LIST:
  566.                sprintf(mybuf,"Receiving %uth newsgroup",RcvLineCount);
  567.                cptr = mybuf;
  568.                cptr2 = "name from server...";
  569.  
  570.               display_msg:;
  571.                X = SideSpace + CharWidth;
  572.                Y = StartPen + 2*LineHeight;
  573.                strcpy(init_msg,cptr);
  574.                strcat(init_msg,"    ");
  575.                j = strlen(init_msg);
  576.                TextOut(hDC,X,Y,init_msg,j);
  577.                strcpy(init_msg,cptr2);
  578.                strcat(init_msg,"    ");
  579.                j = strlen(init_msg);
  580.                TextOut(hDC,X,Y+LineHeight,init_msg,j);
  581.                break;
  582.  
  583.  
  584.              case INIT_DONE:
  585.  
  586.                VertLines = NetDoc.ScYLines;
  587.                HorzChars = NetDoc.ScXChars;
  588. #ifndef MAC
  589.                MyColors[0] = NetUnSubscribedColor;
  590.                MyColors[1] = GetTextColor(hDC);
  591.                MyColors[2] = MyColors[0];
  592.                MyColors[3] = GetBkColor(hDC);
  593.  
  594.                MyBack[0] = MyColors[3];
  595.                MyBack[1] = MyBack[0];
  596.                MyBack[2] = MyColors[1];
  597.                MyBack[3] = MyBack[2];
  598.  
  599.                indicatorwidth = LOWORD(GetTextExtent(hDC,"*",2)) * 7 / 7;
  600. #endif
  601.                LockLine(NetDoc.hCurTopScBlock,NetDoc.TopScOffset,NetDoc.TopScLineID,
  602.                 &BlockPtr,&LinePtr);
  603.  
  604.                /* Update the scroll bar thumb position.                 */
  605.  
  606.                CurPos = NetDoc.TopLineOrd;
  607.                if(CurPos<0) CurPos = 0;
  608.                RangeHigh = NetDoc.TotalLines - VertLines;
  609.                if(RangeHigh<0) RangeHigh = 0;
  610.                SetScrollRange(hWnd,SB_VERT,0,RangeHigh,FALSE);
  611.                SetScrollPos  (hWnd,SB_VERT,CurPos,TRUE);
  612. #ifdef MAC
  613.                myRect.right = NetDoc.DocClipRect.right;
  614.                myRect.top = 0;
  615.                myRect.bottom = LineHeight;
  616. #endif
  617.  
  618.                /* Loop through the lines, painting them on the screen. */
  619.  
  620.                X = SideSpace;
  621.                Xtext = X + indicatorwidth;
  622.                Y = StartPen;
  623.                if(LinePtr->length != END_OF_BLOCK)
  624.                do {
  625.                   MyGroup =  ((TypGroup far *)
  626.                    ((char far *)LinePtr + sizeof(TypLine)));
  627.                   MyLen = MyGroup->NameLen;
  628.                   textptr = (char far *) LinePtr + sizeof(TypLine) +
  629.                    sizeof(TypGroup);
  630.  
  631.                   /* Figure out the color of this line.                 */
  632.  
  633.                   if(MyGroup->Subscribed) {
  634.                      MyColorMask |= SUBSCR_MASK;
  635.                   } else {
  636.                      MyColorMask &= (0xff - SUBSCR_MASK);
  637.                   }
  638.                   if(MyGroup->Selected) {
  639.                      MyColorMask |= SELECT_MASK;
  640.                      Rop = BLACKNESS;
  641.                   } else {
  642.                      MyColorMask &= 0xff - SELECT_MASK;
  643.                      Rop = WHITENESS;
  644.                   }
  645.                   if(MyColorMask != PrevColorMask) {
  646.                      SetTextColor(hDC,MyColors[MyColorMask]);
  647.                      SetBkColor(hDC,MyBack[MyColorMask]);
  648.                      PrevColorMask = MyColorMask;
  649.                   }
  650.  
  651.                   /* Figure out what indicator character to use. */
  652.  
  653.                   indcptr = "    ";
  654.                   if(NetDoc.FindLineID == LinePtr->LineID) {
  655.                      indcptr = ">   ";
  656.                   } else if(MyGroup->HighestPrevSeen) {
  657.                      if(MyGroup->ServerLast > MyGroup->HighestPrevSeen) {
  658.                         indcptr = "*   ";
  659.                      }
  660.                   }
  661.  
  662.                   /* Now write out the line.                            */
  663.  
  664.                   TextOut(hDC,X,Y,indcptr,4);
  665.                   width = LOWORD(GetTextExtent(hDC,textptr,MyLen));
  666.                   TextOut(hDC,Xtext,Y,textptr,MyLen);
  667. #if 0
  668.                   if(MyLen < HorzChars) {
  669. #endif
  670. #ifdef MAC
  671.                      GetPen(&myPoint);
  672.                      myRect.left = myPoint.h;
  673.                      FillRect(&myRect,white);
  674.  
  675.                      myRect.top += LineHeight;
  676.                      myRect.bottom += LineHeight;
  677. #else
  678.                      RestX = Xtext + width;
  679.                      PatBlt(hDC,RestX,Y,myRect.right-RestX,LineHeight,Rop);
  680. #endif
  681. #if 0
  682.                   }
  683. #endif
  684.                   Y += LineHeight;
  685.                } while(--VertLines>0 && NextLine(&BlockPtr,&LinePtr) );
  686.  
  687.                SetTextColor(hDC,MyColors[1]);
  688.                SetBkColor(hDC,MyBack[1]);
  689.  
  690.                /* Blank out bottom and top of screen */
  691.                PatBlt(hDC,0,Y,myRect.right,myRect.bottom-Y,PATCOPY);
  692.                PatBlt(hDC,0,0,myRect.right,TopSpace,PATCOPY);
  693.  
  694.                UnlockLine(BlockPtr,LinePtr,&hBlock,&Offset,&MyLineID);
  695.             }
  696.             EndPaint(hWnd, &ps);
  697.             break;
  698.             }
  699.  
  700.         case WM_ENDSESSION:
  701. #ifndef MAC
  702.             WinHelp(hWndConf,LFN_HELP,HELP_QUIT,0L);
  703.             CloseComm(CommDevice);
  704. #endif
  705.             break;
  706.  
  707.         case WM_DESTROY:
  708.             for(idoc=0; idoc<MAXGROUPWNDS; idoc++) {
  709.                if(GroupDocs[idoc].InUse) {
  710.                   UpdateSeenArts(&(GroupDocs[idoc]));
  711.                }
  712.             }
  713.             if(SaveNewsrc) {
  714.                WriteNewsrc();
  715.             }
  716.             MRRCloseComm();
  717. #if 0
  718.             SendMessage(hDosWnd,WM_KEYDOWN,0x5a,0x2c0001L);
  719.             SendMessage(hDosWnd,WM_CHAR,   0x1a,0x2c0001L);
  720.             SendMessage(hDosWnd,WM_KEYDOWN,0xd,0x1c0001L);
  721.             SendMessage(hDosWnd,WM_CHAR,   0xd,0x1c0001L);
  722. #endif
  723.             PostQuitMessage(0);
  724.             break;
  725.  
  726.         default:
  727.             return (DefWindowProc(hWnd, message, wParam, lParam));
  728.     }
  729.     return (0);
  730. }
  731.  
  732. #ifndef MAC
  733. /*---  FUNCTION: About ---------------------------------------------------
  734.  *
  735.  *  Process messages for "About" dialog box
  736.  */
  737.  
  738. BOOL FAR PASCAL About(hDlg, message, wParam, lParam)
  739. HWND hDlg;
  740. unsigned message;
  741. WORD wParam;
  742. LONG lParam;
  743. {
  744.     switch (message) {
  745.         case WM_INITDIALOG:
  746.             return (TRUE);
  747.  
  748.         case WM_COMMAND:
  749.             if (wParam == IDOK) {
  750.                 EndDialog(hDlg, TRUE);
  751.                 return (TRUE);
  752.             }
  753.             break;
  754.     }
  755.     return (FALSE);
  756. }
  757. #endif
  758.  
  759. /* Function CheckView -----------------------------
  760.  *
  761.  *  This function handles checking and unchecking menu items.
  762.  *
  763.  *    Entry    hWnd     is the window whose menu is to be altered.
  764.  *                      Currently works only for the Net window.
  765.  *
  766.  *    Exit     The appropriate items on the menu have been checked or
  767.  *             unchecked.
  768.  */
  769.  
  770. VOID CheckView(hWnd)
  771. HWND hWnd;
  772. {
  773.    WORD MenuItemToCheck, MenuItemToUncheck;
  774.    HMENU hMenu;
  775.    int CheckIt;
  776.  
  777.    if(ViewNew) {
  778.       CheckIt = MF_CHECKED | MF_BYCOMMAND;
  779.    } else {
  780.       CheckIt = MF_UNCHECKED | MF_BYCOMMAND;
  781.    }
  782. #ifdef MAC
  783.    hMenu = gOptionsMenu;
  784. #else
  785.    hMenu = GetMenu(hWnd);
  786. #endif
  787.    CheckMenuItem(hMenu,IDM_NEW_WIN_GROUP,CheckIt);
  788.  
  789.    if(NewArticleWindow) {
  790.       CheckIt = MF_CHECKED | MF_BYCOMMAND;
  791.    } else {
  792.       CheckIt = MF_UNCHECKED | MF_BYCOMMAND;
  793.    }
  794.    CheckMenuItem(hMenu,IDM_NEW_WIN_ARTICLE,CheckIt);
  795. }
  796.  
  797. /* --- function CrackGroupLine --------------------------------------
  798.  *
  799.  *  Given a line from a .newsrc file, create a text line containing
  800.  *  a structure of type TypGroup containing the same information.
  801.  *  The line is zero-terminated upon input.
  802.  *
  803.  *    Entry    buf      points to a zero-terminated line from .newsrc.
  804.  *             lineptr  points to a place to put the converted textline.
  805.  *
  806.  *    Exit     The line pointed to by "lineptr" now is a TypLine textline
  807.  *             containing a structure of type TypGroup, containing the
  808.  *             information in the input line from .newsrc.
  809.  *
  810.  *  Returns TRUE iff group was subscribed to.
  811.  */
  812. BOOL
  813. CrackGroupLine(buf,lineptr)
  814. char *buf;
  815. TypLine *lineptr;
  816. {
  817.    char *grname = (char *) lineptr + sizeof(TypLine) + sizeof(TypGroup);
  818.    TypGroup *group = (TypGroup *) ((char *) lineptr + sizeof(TypLine));
  819.    TypRange *RangePtr;
  820.    BOOL MoreNums;
  821.    unsigned int MyLength;
  822.  
  823.    group->Subscribed = FALSE;
  824.    group->Selected = FALSE;
  825.    group->NameLen = 0;
  826.    group->SubjDoc = (TypDoc *)NULL;
  827.    group->ServerEstNum = 0;
  828.    group->ServerFirst = 0;
  829.    group->ServerLast = 0;
  830.    group->HighestPrevSeen = 0;
  831.    group->nRanges = 0;
  832.  
  833.    /* Copy group name to output line.                               */
  834.  
  835.    while(*buf && *buf != ':' && *buf != '!') {
  836.       *(grname++) = *(buf++);
  837.       (group->NameLen)++;
  838.    }
  839.    *(grname++) = '\0';
  840.  
  841.    if(!(*buf)) {
  842.       /* Ran off end of line without seeing ':' or '!'.  Error.      */
  843.    } else {
  844.       if(*buf == ':') {
  845.          group->Subscribed = TRUE;
  846.       }
  847.       buf++;
  848.    }
  849.  
  850.    /* Look for the highest article number previously seen, in an
  851.     * entry of form "s" followed by a number.
  852.     */
  853.  
  854.    while(*buf && *buf == ' ') buf++;
  855.    if(*buf == 's') {
  856.       buf++;
  857.       GetNum(&buf,&(group->HighestPrevSeen));
  858.    }
  859.  
  860.    /* Convert the article number ranges to the internal numeric
  861.     * form we use in WinVN.
  862.     */
  863.  
  864.    RangePtr = (TypRange *) ((char *) lineptr + sizeof(TypLine) +
  865.     RangeOffset(group->NameLen));
  866.  
  867.    RangePtr->Last = RangePtr->First = 0;
  868.  
  869.    MoreNums = TRUE;
  870.    while(MoreNums) {
  871.       while(*buf && (*buf == ' ' || *buf == ',') ) buf++;
  872.       if(GetNum(&buf,&(RangePtr->First))) {
  873.          /* We have the first number in a range.                     */
  874.          (group->nRanges)++;
  875.          RangePtr->Last = RangePtr->First;
  876.          if(*buf == '-') {
  877.             buf++;
  878.             if(GetNum(&buf,&(RangePtr->Last))) {
  879.                RangePtr++;
  880.             /* at this point, we are positioned just after a range */
  881.             } else {
  882.                RangePtr->Last = RangePtr->First;
  883.                MoreNums = FALSE;
  884.             }
  885.          } else if(*buf == ',') {
  886.             /* We have a single number "n"; interpret as the range "n-n".
  887.              */
  888.             RangePtr++;
  889.          } else {
  890.             /* That must have been the last number.                  */
  891.             MoreNums = FALSE;
  892.          }
  893.       } else {
  894.          MoreNums = FALSE;
  895.       }
  896.    }
  897.    if(group->nRanges == 0) (group->nRanges)++;
  898.  
  899.    MyLength = sizeof(TypLine) + RangeOffset(group->NameLen) +
  900.     sizeof(TypRange)*(group->nRanges) + sizeof(int);
  901.  
  902.    lineptr->length = MyLength;
  903.    lineptr->LineID = NextLineID++;
  904.    *(int *) ( (char *)lineptr + MyLength - sizeof(int) ) = MyLength;
  905.  
  906.    return(group->Subscribed);
  907. }
  908.  
  909. /*-- function CursorToTextLine ----------------------------------------
  910.  *
  911.  *   Routine to locate a text line in a document, based on the
  912.  *   cursor position.  Used to figure out which line is being selected
  913.  *   when a user clicks a mouse button.
  914.  *
  915.  *   Entry    X, Y    are the position of the cursor.
  916.  *            DocPtr  points to the current document.
  917.  *
  918.  *   Exit     *LinePtr points to the current line, if one was found.
  919.  *            *BlockPtr points to the current block, if found.
  920.  *            Function returns TRUE iff a line was found that corresponds
  921.  *              to the cursor position.
  922.  */
  923. BOOL
  924. CursorToTextLine(X,Y,DocPtr,BlockPtr,LinePtr)
  925. int X;
  926. int Y;
  927. TypDoc *DocPtr;
  928. TypBlock far **BlockPtr;
  929. TypLine far **LinePtr;
  930. {
  931.    int found;
  932.    int SelLine;
  933.  
  934.    if(Y < TopSpace || Y > TopSpace + DocPtr->ScYLines*LineHeight ||
  935.     X < SideSpace) {
  936.       /* Cursor is in no-man's-land at edge of window.               */
  937.       found = FALSE;
  938.    } else {
  939.       found = TRUE;
  940.       SelLine = (Y - TopSpace) / LineHeight;
  941.  
  942.       LockLine(DocPtr->hCurTopScBlock,DocPtr->TopScOffset,DocPtr->TopScLineID,
  943.        BlockPtr,LinePtr);
  944.  
  945.       for(found=TRUE,il=0; il<SelLine; il++) {
  946.          if(!NextLine(BlockPtr,LinePtr)) {
  947.             found = FALSE;     /* ran off end of document */
  948.             break;
  949.          }
  950.       }
  951.    }
  952.    return(found);
  953. }
  954.  
  955. /*-- function ReadNewsrc ----------------------------------------------
  956.  *
  957.  *    Reads NEWSRC into the Net document.
  958.  *    This routine opens NEWSRC, reads & parses the lines into the NetDoc
  959.  *    document, and closes the file.  One call does it all.
  960.  *
  961.  *    Entry    The NetDoc document is assumed to be initialized.
  962.  *
  963.  *    Exit     Returns TRUE if all went well, else zero.
  964.  */
  965. int
  966. ReadNewsrc()
  967. {
  968.    TypBlock far *BlockPtr;
  969.    TypLine *LocalLinePtr, far *GroupPtr;
  970.    HANDLE hLine;
  971.    HANDLE hBlock;
  972.    HANDLE hRetCode;
  973.    unsigned int Offset;
  974.    TypLineID MyLineID;
  975.    char mes[60];
  976.    char far *chptr;
  977.    int mylen,j;
  978. #define  TEMPBUFSIZE   1240
  979.    char mybuf[TEMPBUFSIZE];
  980.    char *grname;
  981.    TypMRRFile *MRRFile;
  982.    int returned;
  983.  
  984.  
  985.    LockLine(NetDoc.hCurAddBlock,NetDoc.AddOffset,NetDoc.AddLineID,&BlockPtr,&GroupPtr);
  986.    NetDoc.hDocWnd = hWndConf;
  987.  
  988.    hLine = LocalAlloc(LMEM_MOVEABLE,TEMPBUFSIZE );
  989.    LocalLinePtr = (TypLine *) LocalLock(hLine);
  990.  
  991.    strcpy(mybuf,"newsrc");
  992.    hRetCode = MRROpenFile(mybuf,0,OF_READ,&MRRFile);
  993.    if((int)hRetCode <= 0) {
  994.       return FALSE;
  995. #if 0
  996.       sprintf(mes,"Unable to open the NEWSRC file; code=%d",(int)hRetCode);
  997.       MessageBox(hWndConf,mes,mybuf,MB_OK);
  998. #endif
  999.    } else {
  1000.       /* Loop to read lines, convert them to internal format, and
  1001.        * insert them into the NetDoc document.
  1002.        */
  1003.  
  1004.       while((returned=MRRReadLine(MRRFile,mybuf,TEMPBUFSIZE)) > (-1)) {
  1005.          mybuf[returned] = '\0';
  1006.          if(CrackGroupLine(mybuf,LocalLinePtr)) {
  1007.             NetDoc.ActiveLines++;
  1008.          }
  1009.          AddLine(LocalLinePtr,&BlockPtr,&GroupPtr);
  1010.       }
  1011.       MRRCloseFile(MRRFile);
  1012.    }
  1013.  
  1014.    /* Change the title of the Net document.  I suppose that,
  1015.     * strictly speaking, this oughtn't be done in this routine.
  1016.     */
  1017.    SetNetDocTitle();
  1018.    UnlockLine(BlockPtr,GroupPtr,&hBlock,&Offset,&MyLineID);
  1019.  
  1020.    NetDoc.hCurTopScBlock = NetDoc.hFirstBlock;
  1021.    NetDoc.TopScOffset = sizeof(TypBlock);
  1022.    NetDoc.TopScLineID = 0L;
  1023.  
  1024.    return(TRUE);
  1025. }
  1026.  
  1027. char *ltoa();
  1028.  
  1029. /*--- function WriteNewsrc ---------------------------------------------
  1030.  *
  1031.  *  Write out a NEWSRC file, based on the information in the
  1032.  *  NetDoc document.  Use the standard Unix "rn" format for .newsrc.
  1033.  *
  1034.  *    Entry    no parameters
  1035.  *             NetDoc   has the group information.
  1036.  *
  1037.  *    Exit     The NEWSRC file has been written.
  1038.  */
  1039. void
  1040. WriteNewsrc()
  1041. {
  1042.    TypBlock far *BlockPtr;
  1043.    TypLine  far *LinePtr;
  1044.    HANDLE hLine;
  1045.    HANDLE hBlock;
  1046.    unsigned int Offset;
  1047.    TypLineID MyLineID;
  1048.    char mes[60], mybuf[60];
  1049.    char far *fromptr;
  1050.    char *toptr;
  1051.    char *NewsLine;
  1052.    HANDLE hRetCode;
  1053.    TypMRRFile *MRRFile;
  1054.    int returned;
  1055.    int nranges, nchars;
  1056.    TypGroup far *Group;
  1057.    TypRange far *RangePtr;
  1058.    BOOL firstrange;
  1059.  
  1060.    LockLine(NetDoc.hFirstBlock,sizeof(TypBlock),0L,&BlockPtr,&LinePtr);
  1061.  
  1062.    hLine = LocalAlloc(LMEM_MOVEABLE,BLOCK_SIZE);
  1063.    NewsLine = (char *) LocalLock(hLine);
  1064.  
  1065.    strcpy(mybuf,"newsrc");
  1066.    hRetCode = MRROpenFile(mybuf,0,OF_CREATE,&MRRFile);
  1067.    if((int)hRetCode < 0) {
  1068.       sprintf(mes,"MRROpenFile returned %d",hRetCode);
  1069.       MessageBox(hWndConf,mes,mybuf,MB_OK);
  1070.    } else {
  1071.       do {
  1072.          toptr = NewsLine;
  1073.          Group = (TypGroup far *) ((char far *)LinePtr + sizeof(TypLine));
  1074.  
  1075.          /* Copy group name                                          */
  1076.          fromptr = (char far *)LinePtr + sizeof(TypLine) + sizeof(TypGroup);
  1077.          while(*(toptr++) = *(fromptr++));
  1078.          toptr--;
  1079.  
  1080.          /* Affix : or ! depending upon whether subscribed.          */
  1081.          *(toptr++) = (char) (Group->Subscribed ? ':' : '!');
  1082.          *(toptr++) = ' ';
  1083.  
  1084.          /* If we know the highest article number on the server,
  1085.           * output it preceded by an "s".
  1086.           */
  1087.          if(Group->ServerLast) {
  1088.             *(toptr++) = 's';
  1089.             ltoa((unsigned long)Group->ServerLast,toptr,10);
  1090.             while(*toptr) toptr++;
  1091.             *(toptr++) = ' ';
  1092.          }
  1093.  
  1094.          /* Affix ranges of articles read.                          */
  1095.          firstrange = TRUE;
  1096.          nranges = Group->nRanges;
  1097.          RangePtr = (TypRange far *) ((char far *)Group + RangeOffset(Group->NameLen));
  1098.  
  1099.          while(nranges--) {
  1100.             /* Write out ',' if not first range of articles.         */
  1101.  
  1102.             if(!firstrange) {
  1103.                *(toptr++) = ',';
  1104.             } else {
  1105.                firstrange = FALSE;
  1106.             }
  1107.             /* Write out first article in a range.                   */
  1108.  
  1109.             ltoa((unsigned long)RangePtr->First,toptr,10);
  1110.             while(*toptr) toptr++;
  1111.  
  1112.             /* If the range is of form "n-n", just put out "n"       */
  1113.  
  1114.             if(RangePtr->First != RangePtr->Last) {
  1115.                /* Put out the hyphen in middle of range.                */
  1116.                *(toptr++) = '-';
  1117.                /* Put out the last article in a range.                  */
  1118.                ltoa((unsigned long)RangePtr->Last,toptr,10);
  1119.                while(*toptr) toptr++;
  1120.             }
  1121.             RangePtr++;
  1122.          }
  1123.          MRRWriteLine(MRRFile,NewsLine,toptr-NewsLine);
  1124.  
  1125.       } while(NextLine(&BlockPtr,&LinePtr));
  1126.       UnlockLine(BlockPtr,LinePtr,&hBlock,&Offset,&MyLineID);
  1127.       MRRCloseFile(MRRFile);
  1128.    }
  1129. }
  1130.  
  1131. /*--- function SetNetDocTitle -------------------------------------------
  1132.  *
  1133.  */
  1134. void
  1135. SetNetDocTitle()
  1136. {
  1137.    char mybuf[120];
  1138.  
  1139.    sprintf(mybuf,"WinVN:  %d groups; %d subscribed",NetDoc.TotalLines,
  1140.     NetDoc.ActiveLines);
  1141.    SetWindowText(hWndConf,mybuf);
  1142.  
  1143. }
  1144.  
  1145. /*--- function SetLineFlag --------------------------------------------
  1146.  *
  1147.  *  Set some flag in a line in a document.
  1148.  *
  1149.  *  Entry   Doc      points to the document.
  1150.  *          LinePtr  points to th line.
  1151.  *
  1152.  *  Exit    lFlag    says what to do.
  1153.  */
  1154. void
  1155. SetLineFlag(TypDoc *Doc, TypBlock far **BlockPtr, TypLine far **LinePtr, int wFlag, int wValue)
  1156. {
  1157.    switch(wFlag) {
  1158.       case LINE_FLAG_SET:
  1159.          ( (TypGroup far *)( ((char far *)*LinePtr) + sizeof(TypLine) ) )
  1160.             ->Selected = wValue;
  1161.          break;
  1162.    }
  1163. }
  1164.  
  1165.  
  1166. /*--- function GroupAction --------------------------------------------
  1167.  *
  1168.  *  Perform some action on a group that is specified by a pointer
  1169.  *  to a line in the Net document.
  1170.  *  Typically called for each line in the Net document by
  1171.  *  ForAllLines.
  1172.  *
  1173.  *  Entry   Doc      points to NetDoc
  1174.  *          LinePtr  points to a line in that document.
  1175.  *          lFlag    indicates what to do with that line.
  1176.  */
  1177. void
  1178. GroupAction(TypDoc *Doc, TypBlock far **BlockPtr, TypLine far **LinePtr, int wFlag, int wValue)
  1179. {
  1180.  
  1181.    switch(wFlag) {
  1182.       case GROUP_ACTION_SUBSCRIBE:
  1183.       case GROUP_ACTION_UNSUBSCRIBE:
  1184.          if(((TypGroup far *)( ((char far *)*LinePtr) + sizeof(TypLine)) )
  1185.                    ->Selected) {
  1186.             ((TypGroup far *)( ((char far *)*LinePtr) + sizeof(TypLine)) )
  1187.                    ->Subscribed = wValue;
  1188.             AddGroupToTable((char far *)*LinePtr);
  1189.             DeleteLine(BlockPtr,LinePtr);
  1190.          }
  1191.          break;
  1192.  
  1193.    }
  1194. }
  1195.  
  1196. /****************************************************************************
  1197.  
  1198.    FUNCTION:   MakeHelpPathName
  1199.  
  1200.    PURPOSE:    HelpEx assumes that the .HLP help file is in the same
  1201.                directory as the HelpEx executable.This function derives
  1202.                the full path name of the help file from the path of the
  1203.                executable.
  1204.  
  1205.    Taken from HELPEX.C, from the MS Windows SDK.
  1206.  
  1207. ****************************************************************************/
  1208.  
  1209. void MakeHelpPathName(szFileName,maxchars)
  1210. char * szFileName;
  1211. int maxchars;
  1212. {
  1213.    char *  pcFileName;
  1214.    int     nFileNameLen;
  1215.  
  1216.    nFileNameLen = GetModuleFileName(hInst,szFileName,maxchars);
  1217.    pcFileName = szFileName + nFileNameLen;
  1218.  
  1219.    while (pcFileName > szFileName) {
  1220.        if (*pcFileName == '\\' || *pcFileName == ':') {
  1221.            *(++pcFileName) = '\0';
  1222.            break;
  1223.        }
  1224.    nFileNameLen--;
  1225.    pcFileName--;
  1226.    }
  1227.  
  1228.    if ((nFileNameLen+13) < maxchars) {
  1229.        lstrcat(szFileName, LFN_HELP);
  1230.    }
  1231.  
  1232.    else {
  1233.        lstrcat(szFileName, "?");
  1234.    }
  1235.  
  1236.    return;
  1237. }
  1238.  
  1239.  
  1240. /* Last line of WVUSENET.C */
  1241.